import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.Random;
import java.util.Calendar;
import java.util.Set;

import com.mongodb.BasicDBList;
import com.mongodb.Mongo;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBCursor;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
import com.mongodb.util.JSON;
import com.mongodb.CommandResult;

public class MongoInsertTest {

	static Mongo m;
	static DB db;
	static DBCollection coll;

	public static void main(String[] args) {
		try {
			m = new Mongo("localhost");
			db = m.getDB("mydb");
			coll = db.getCollection("people");
			coll.ensureIndex((DBObject) JSON.parse("{ 'loc' : '2d' }"));

			// clean();
			// insertGeo();
			// insertSimple();

			// TODO: We are not getting distance information in the result. How
			// do we get that, and in sorted fashion.

			 //searchWithNearSphere();
			searchWithGeoNear();
			// findMany();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (MongoException e) {
			System.out.println("Exception Message: " + e.getMessage());
			e.printStackTrace();
		}

	}

	static void insertGeo() {
		BasicDBObject doc = new BasicDBObject();

		// doc.put("loc", val);
		// coll.insert((DBObject)
		// JSON.parse("{'loc': { 'lat' : 61.1661, 'long' : -149.96 } }"));
		Random rand = new Random();
		long startTime = Calendar.getInstance().getTimeInMillis();
		for (int i = 0; i < 3000000; i++) {
			double lat = rand.nextDouble() * 180 - 90; // This calculation may
														// be wrong.

			BigDecimal bd1 = new BigDecimal(lat).setScale(2,
					RoundingMode.HALF_EVEN);
			lat = bd1.doubleValue();

			if (lat <= -90)
				lat = lat + 0.5;
			if (lat >= 90)
				lat = lat - 0.5;

			double lon = rand.nextDouble() * 360 - 180; // This calculation may
														// be wrong.

			BigDecimal bd = new BigDecimal(lon).setScale(2,
					RoundingMode.HALF_EVEN);
			lon = bd.doubleValue();

			if (lon <= -180)
				lon = lon + 0.5;
			if (lon >= 90)
				lon = lon - 0.5;

			// System.out.println("lon / lat = " + lon + " / " + lat);
			// lat=1; lon=1;
			coll.insert(
					(DBObject) JSON.parse("{'loc' : [ " + lon + " , " + lat
							+ " ]}"), WriteConcern.SAFE);
		}

		long timeTaken = Calendar.getInstance().getTimeInMillis() - startTime;
		System.out.println("Total Time Taken to Insert (milliseconds): "
				+ timeTaken);

		/*
		 * DBCollection coll = db.getCollection("MyColl");
		 * 
		 * 
		 * BasicDBObject nativeQuery = new BasicDBObject();
		 * 
		 * 
		 * nativeQuery.put("location.crd", new BasicDBObject("$near",
		 * "["+locationLongitude+", "+locationLatitude+"]"));
		 * 
		 * 
		 * DBCursor cursor = coll.find(nativeQuery);
		 */
	}

	static void clean() {
		// clear records if any
		DBCursor cur = coll.find();
		while (cur.hasNext())
			coll.remove(cur.next());
	}

	static void insertSimple() {
		// Insert a object

		BasicDBObject doc = new BasicDBObject();

		doc.put("name", "MyLocations");
		doc.put("lat", 10);
		doc.put("lon", 20);
		coll.insert(doc);

	}

	static void insertMany() {
		// Add some Basic Objects

		for (int i = 0; i < 100; i++) {
			coll.insert(new BasicDBObject().append("i", i));
		}

	}

	static void findOne() {
		// Lets find first object
		DBObject myDoc = coll.findOne();
		System.out.println("First Object" + myDoc);
	}

	static void findMany() {
		// Find All Objects
		DBCursor cur = coll.find();

		while (cur.hasNext()) {
			System.out.println(cur.next());
		}

		System.out.println("Total Documents: " + coll.getCount());
	}

	static void simpleQuery() {
		// Query for a single Object
		BasicDBObject query = new BasicDBObject();
		query.put("i", 71);
		DBCursor cur = coll.find(query);
		while (cur.hasNext()) {
			System.out.println("Queried and found: " + cur.next());
		}
	}

	static void rangeQuery() {
		// Another Query
		// BasicDBObject query1 = new BasicDBObject();
		BasicDBObject query = new BasicDBObject();
		query.put("i", new BasicDBObject("$gt", 50)); // e.g. find all where
														// i > 50
		DBCursor cur = coll.find(query);

		while (cur.hasNext()) {
			System.out.println("Another Query Result: " + cur.next());
		}

		// Another Query
		query = new BasicDBObject();
		query.put("i", new BasicDBObject("$gt", 20).append("$lte", 30)); // i.e.
																			// 20
																			// <
																			// i
																			// <=
																			// 30
		DBCursor cur1 = coll.find(query);

		while (cur1.hasNext()) {
			System.out.println("One More Query: " + cur1.next());
		}
	}

	static void searchWithNearSphere() {
		// 1 radian = 3959 miles (6371 km).
		// So, 100 miles = 100/3959 = 0.0253 radians.
		// So, 1000 miles = 1000/3959 = 0.253 radians.
		System.out.println("searchWithNearSphere: Query to run: "
				+ "{'$nearSphere' : [ 78 , 10 ] , '$maxDistance' : 0.253}");

		long startTime = Calendar.getInstance().getTimeInMillis();
		// Must use long/lat format, and not lat/long.
		// Valid bounds for latitude is -90/90.
		// Valid bounds for longitude is -180/180
		DBCursor cur = coll.find(new BasicDBObject("loc", JSON.parse("{'$nearSphere' : [ 78 , 10 ] , '$maxDistance' : 0.253}"))).limit(100000);
		long timeTaken = Calendar.getInstance().getTimeInMillis() - startTime;
		System.out.println("Total Time Taken for 'searchWithNearSphere(milliseconds): " + timeTaken);

		System.out.println("Total Documents Found: " + cur.count());
		System.out.println("Total Documents Found: " + cur.length());
		System.out.println("Total Documents Found: " + cur.size());

		/*
		 * while (cur.hasNext()) { System.out.println(cur.next()); }
		 * 
		 * System.out.println("Total Documents Found: " + cur.size());
		 */

	}

	static void searchWithGeoNear() {
		BasicDBObject cmd = new BasicDBObject();
		cmd.put("geoNear", "people");
		int coord[] = { 78, 10 };
		cmd.put("near", coord);
		cmd.put("num", 1000);
		cmd.put("maxDistance", 0.0253);
		cmd.put("spherical", true);


		long startTime = Calendar.getInstance().getTimeInMillis();
		CommandResult r = db.command(cmd);
		long timeTaken = Calendar.getInstance().getTimeInMillis() - startTime;
		System.out.println("Total Time Taken for 'searchWithGeoNear(milliseconds): " + timeTaken);
		System.out.println(r.ok());
		System.out.println(r.getErrorMessage());
		// System.out.println(r.toString());

		// print(r);//Print all the returned data.

		com.mongodb.BasicDBList resultsBasicDBList = (BasicDBList) r.get("results");
		System.out.println("Total Results: " + resultsBasicDBList.size());

		// System.out.println("Results: " + resultsBasicDBList); //This will
		// print the value of 'results' key.

		// print(resultsBasicDBList);//This will print individual values.
	}

	public static void print(DBObject doc) {
		Set<String> allKeys = doc.keySet();
		Iterator<String> it = allKeys.iterator();
		while (it.hasNext()) {

			Object o = it.next();
			String temp = (String) o; // it.next();
			// System.out.println("K CLASS: " + o);
			// System.out.println("V CLASS: " + doc.get(temp).getClass());
			System.out.print(temp + "-");

			if (doc.get(temp) instanceof BasicDBObject) {
				System.out.println("\n");
				print((DBObject) doc.get(temp));
			} else {
				System.out.println(doc.get(temp));
				if (temp.toString().equalsIgnoreCase("loc"))
					System.out.println("-------------");
			}
		}
	}
}
